import { dom } from '@knno/dom';
import { empty } from '../empty/empty';
import { tableStyle } from './table.css';
import { genId } from '../../tools/utils';
import { genWidth, GridBase, GridBaseData } from '../grid/gridBase';
import { DATA } from '../symbol';

export interface Table {
	[DATA]: GridBaseData & { id: string };
}

export class Table extends GridBase {
	constructor() {
		const id = 'tb-' + genId();
		const colGroup = dom.colgroup();
		super(dom.div(tableStyle).append(dom.style(), dom.table(id).append(colGroup, dom.thead(), dom.tbody())));
		this[DATA].id = id;
		this.headLine(true).showEmpty(true).columnSize('auto');
	}

	protected override setActiveRow(rowIdx: number | null, focus: boolean): void {
		this.query('tbody>tr').removeClass('active');
		if (rowIdx !== null) {
			const tr = this.query(`tbody>tr.r${rowIdx}`);
			tr.addClass('active');
			if (focus) {
				const headerHeight = this.query('thead').height();
				if (tr.top() + tr.height() > this.scrollTop() + this.innerHeight()) {
					// after the viewport.
					this.scrollTop(tr.top() + tr.height() - this.innerHeight());
				}
				if (tr.top() < this.scrollTop() + headerHeight) {
					// before the viewport.
					this.scrollTop(tr.top() - headerHeight);
				}
			}
		}
		this[DATA].actived = rowIdx;
	}

	protected override renderSelect() {
		const columns = this.getColumns();
		const th = this.query('thead>tr.head>th.c0');
		if (th.length > 0) {
			th.html('').append(columns[0].formatter?.({}, null, -1, 0) ?? []);
		}
		const tds = this.query(`tbody>tr>td.c0`);
		for (let i = 0; i < tds.length; i++) {
			const td = dom.wrap(tds[i]);
			td.html('').append(columns[0].formatter?.({}, null, i, 0) ?? []);
		}
	}

	protected override genStyle() {
		const columns = this.adjustColumnSize();
		const cols = this.query('colgroup>col');
		cols.forEach((col, i) => {
			const c = columns[i];
			const width = genWidth(c.width);
			(col as HTMLTableColElement).style.width = width;
		});

		// set fixed columns;
		let text = '';
		const rulerCells = this.query('thead>tr>td');
		for (let i = 0; i < this.fixColumns() && i < columns.length; i++) {
			text += `.${tableStyle} table.${this[DATA].id} .c${i}{z-index:1;position:sticky;left:${
				(rulerCells[i] as HTMLElement).offsetLeft
			}px;}`;
		}
		this.query('style').html(text);
		return false;
	}

	protected override renderBody() {
		const columns = this.getColumns();
		const lastCol = columns[columns.length - 1];
		const tbody = this.query('tbody');
		tbody.html('');
		this.query(':scope>div.empty').detach();
		const data = this.getSortedData();
		if (data.length > 0) {
			tbody.append(
				...data.map((row, rowIdx) => {
					const line = dom.tr(
						`r${rowIdx}`,
						rowIdx === this.active() ? 'active' : '',
						this.isRowDisable(row) ? 'disable' : ''
					);
					let sectionValue: unknown;
					// eslint-disable-next-line @typescript-eslint/no-explicit-any
					if (this[DATA].section && (sectionValue = (row as any)[this[DATA].section.key])) {
						line.append(
							this.renderSection(dom.td().attr('colspan', columns.length + ''), sectionValue, row, rowIdx, lastCol)
						);
					} else {
						line.append(...columns.map((col, colIdx) => this.renderCell(dom.td(), row, rowIdx, col, colIdx)));
					}
					return line;
				})
			);
		} else if (this.showEmpty()) {
			this.append(dom.div('empty').append(empty()));
		}
	}

	protected override renderHead() {
		const columns = this.getColumns();
		const thead = this.query('thead');
		thead.html('');
		const colGroup = this.query('colgroup');
		colGroup.html('');
		if (this.headLine() && columns.length > 0) {
			thead.append(dom.tr('head').append(...columns.map((col, colIdx) => this.renderHeadCell(dom.th(), col, colIdx))));
		}
		thead.append(
			dom
				.tr()
				.css({
					height: '0',
					overflow: 'hidden',
				})
				.append(
					...columns.map(() =>
						dom.td().css({
							boxSizing: 'border-box',
							padding: '0',
							margin: '0',
							height: '0',
							overflow: 'hidden',
						})
					)
				)
		);
		colGroup.append(...columns.map(() => dom.col()));
	}
}

export function table(...className: string[]): Table {
	return new Table().addClass(...className);
}
